package com.puzzlebazar.client.squaregrid.control;

import java.util.Iterator;

import com.google.gwt.user.client.DOM;
import com.puzzlebazar.client.squaregrid.view.ComponentHighlighter;
import com.puzzlebazar.client.util.ConcurrentVector;
import com.puzzlebazar.client.util.Vec2i;
import com.google.gwt.user.client.Element;
import com.google.gwt.event.dom.client.MouseEvent;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerRegistration;

public class RectangleControl {

	private final ComponentHighlighter highlighter;
	private final ControlView controller;

	private final CellCallback cellCallback = new CellCallback();
	private final MyMouseUpHandler mouseUpHandler = new MyMouseUpHandler();
	private HandlerRegistration mouseUpHandlerRegistration = null;
	
	private final ConcurrentVector< RectangleCallback > callbacks = new ConcurrentVector< RectangleCallback >();
	
	private Vec2i startCorner = new Vec2i(-1,-1);
	private Vec2i oppositeCorner = new Vec2i(-1,-1);
	
	private Vec2i lastStartCorner = new Vec2i(-1,-1);
	private Vec2i lastOppositeCorner = new Vec2i(-1,-1);
	
	private static class MultiHandler { 

		public static boolean rectangleBegin( ConcurrentVector<RectangleCallback> collection, Vec2i loc, MouseEvent<? extends EventHandler> event ) {
			boolean allowed = false;
			for (Iterator<RectangleCallback> iterator = collection.iterator(); iterator.hasNext();) {
				RectangleCallback callback = iterator.next();
				if( callback.rectangleBegin( loc, event ) ) allowed = true;
			}
			return allowed;
		}
		
		public static boolean rectangleChange( ConcurrentVector<RectangleCallback> collection, Vec2i loc1, Vec2i loc2, MouseEvent<? extends EventHandler> event ) {
			boolean allowed = false;
			for (Iterator<RectangleCallback> iterator = collection.iterator(); iterator.hasNext();) {
				RectangleCallback callback = iterator.next();
				if( callback.rectangleChange( loc1, loc2, event ) ) allowed = true;
			}
			return allowed;
		}

		public static void rectangleEnd( ConcurrentVector<RectangleCallback> collection, Vec2i loc1, Vec2i loc2, MouseEvent<? extends EventHandler> event ) {
			for (Iterator<RectangleCallback> iterator = collection.iterator(); iterator.hasNext();) {
				RectangleCallback callback = iterator.next();
				callback.rectangleEnd( loc1, loc2, event );
			}
		}
	}	
	
	
	/**
	 * Creates a controller that can automatically highlight cells, edges and vertices
	 * as the user moves his mouse around
	 * 
	 * @param highlighter The object responsible of highlighting the various components
	 * @param controller The controller decorator to use
	 */
	public RectangleControl(
			ComponentHighlighter highlighter,
			ControlView controller) {
		
		this.highlighter = highlighter;
		this.controller = controller;
		
		controller.addCellCallback( cellCallback );
		mouseUpHandlerRegistration = controller.addMouseUpHandler( mouseUpHandler );
	}


	private class CellCallback extends ComponentCallbackAdapter {
	
		@Override
		public boolean mouseDown(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			if( !MultiHandler.rectangleBegin(callbacks, cellLoc, event) )
				return false;			
			try {
				startCorner.copy( cellLoc );
				oppositeCorner.copy( cellLoc );
				updateHighlight();
				DOM.setCapture( (Element) Element.as( event.getNativeEvent().getEventTarget() ) );
				return true;
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return false;
		}

		@Override
		public boolean canHandleMouseEnter(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {			
			return startCorner.x != -1;
		}

		@Override
		public boolean mouseEnter(Vec2i cellLoc, MouseEvent<? extends EventHandler> event ) {
			if( startCorner.x == -1 ) return false;
			if( !MultiHandler.rectangleChange(callbacks, startCorner, cellLoc, event) )
				return false;			
			try {
				oppositeCorner.copy( cellLoc );
				updateHighlight();
			} catch (Exception exception) {
				exception.printStackTrace();
			}
			return true;
		}

	}	

	private class MyMouseUpHandler implements MouseUpHandler {

		@Override
		public void onMouseUp(MouseUpEvent event) {
			if( startCorner.x == -1 ) return;
			
			MultiHandler.rectangleEnd(callbacks, startCorner, oppositeCorner, event);
			DOM.releaseCapture( (Element) Element.as( event.getNativeEvent().getEventTarget() ) );
			try {
				startCorner.x = -1;
				updateHighlight();
			} catch (Exception exception) {
				exception.printStackTrace();
			}
		}
		
	}

	/**
	 * Delete old highlighted rectangle, draw the newly highlighted rectangle
	 * 
	 * @throws Exception
	 */
	private void updateHighlight() throws Exception {
		if( lastStartCorner.equals(startCorner) && 
				lastOppositeCorner.equals(oppositeCorner) )
			return;
			
		if( lastStartCorner.x != -1 )		
			highlighter.unhighlightCells( lastStartCorner, lastOppositeCorner );
		
		if( startCorner.x != -1 )
			highlighter.highlightCells( startCorner, oppositeCorner );
		
		lastStartCorner.copy(startCorner);
		lastOppositeCorner.copy(oppositeCorner);			
	}

	/**
	 * Adds an object responsible of controlling rectangle-related events
	 * 
	 * @param callback Callback object to add
	 */
	public void addRectangleCallback(RectangleCallback callback) {
		callbacks.add( callback );
	}		
	

	/**
	 * Removes an object responsible of controlling rectangle-related events
     *
	 * @param callback Callback object to remove
	 */
	public void deleteRectangleCallback(RectangleCallback callback) {
		callbacks.remove( callback );
	}
	
	/**
	 * Removes this rectangle highlighting view
	 */
	public void release() {
		controller.removeCellCallback( cellCallback );
		if( mouseUpHandlerRegistration != null )
			mouseUpHandlerRegistration.removeHandler();
		mouseUpHandlerRegistration = null;
		callbacks.clear();
	}

	
}